function parityPerceptron = parityPerceptron(N,b=1,e=2, epocas=200)
%alpha=0.9;
TANH=1;
EXP=2;
LINEAL=3;
FUNC=TANH;

N=[1 N 1];					
% la funcion es de R->R por lo que es siempre UNA entrada y UNA salida, lo que pasamos por parametro ahora son 
% las capas intermedias por ejemplo [2] significa una capa oculta con 2 neuronas (mas la capa de entrada y la capa de salida con una neurona cada una)

for c=1:length(N)-1
	w{c} = rand(N(c+1),N(c)+1)-0.5; %el +1 es por el umbral.
	%w{c} = w{c} ./ 100;
	%ALAN: en una de las filminas dice que hay que adaptar los w iniciales para que h(i)=w*psi sea un poco menor a 1... trate poner un w inicial manual para que de por ahi pero tampoco funciono!
	%w{c} = zeros(N(c+1),N(c)+1)+0.0962;
endfor
% como patrones usamos un numero arbitrario de puntos de la funcion, en este caso discretizados cada 0.1 unidad
%psi=(30:0.5:45)'; 
%ALAN: hice que se agreguen mas patrones de la parte final del dominio (no soluciono el problema) pero creo que hay que hacerlo asi
%psi=[(10:0.5:25)';(25:0.2:32)';(32:0.1:45)']; 
%psi=[(15:0.3:19)';(19:0.1:21)';(21:0.3:28)';(28:0.1:30)';(30:0.3:35)';(35:0.1:45)'];
%psi=[(10:0.1:45)'];
%psi=[(25:0.5::0.01:32)'];
%psi=[(15:0.5:19)';(19:0.1:21)';(21:0.5:25)';(25:0.1:28)';(28:0.5:32)';(32:0.1:35)';(35:0.5:38)';(38:0.1:41)';(41:0.5:45)'];
%psi=[(-1:0.1:-0.8)';(-0.8:0.001:-0.6)';(-0.6:0.1:0.2)';(0.2:0.001:0.5)';(0.5:0.1:1)'];
psi=[(-2*pi:0.1:2*pi)'];
%psi=[(-1:0.01:1)'];
%SALIDAS CORRECTAS
%Para las salidas se aplica directamente la funcion al vector (funcion nro 9)
s=f(psi); 		

%Actualmente esta funcion devuelve psi completo, mas adelante hay que elegir un subconjunto
psiTraining=getPsiTraining(psi);	
sTraining=f(psiTraining);		

%Por ahora nada de test, hay que hacer que funcione el entrenamiento primero	
%psiTest=getPsiTest(psi);		
%sTest=f(psiTest);

%psi = agregarUmbral(psi);
%psiTraining = agregarUmbral(psiTraining);	

%psiTraining(1,:)
%psiTest = agregarUmbral(psiTest);

% impresiones de prueba
%psi;
%s;
%psiTraining;
%sTraining;
%psiTest;
%sTest;
%w
j=0;
errorAnt=0;
do
  indices = randperm(rows(psiTraining));

  erroracum=0;
  %wold=w;
  for ind=1:rows(psiTraining)
	i=indices(ind);
	h=[];
	v=[];
	v{1}=horzcat(psiTraining(i,:),-1); %seteo el inicixal
	
	for k=2:length(N)-1
		%Voy calculando para adelante con los w y v que tengo
		h{k} = (w{k-1}*v{k-1}')';			%'
		v{k} = applyG(b, h{k}, FUNC);			
		% calculo todo menos la ultima capa, la anteultima capa es v(k) y w(k). despues de este for es como si fuese un perceptron simple
		v{k} = horzcat(v{k},-1);
	endfor
	% v{k+1} es la salida de la ultima capa (el resultado), w{k} es los pesos entre la ultima capa oculta y la neurona de salida
	%ACA ANTES ESTABA LA TANH PERO COMO ES SINUSOIDAL ESTO TIENE QUE SER LINEAL PARA QUE NO ACOTE, PORQUE EL RESULTADO PUEDE IRSE AL CHOTO Y LA TANH TE LO ACOTA. SUPONGO QUE PARA EL DELTA TAMBIEN YA QUE ES G'
	h{k+1} = (w{k}*v{k}')';			%'
	v{k+1} = applyG(b,h{k+1}, LINEAL); 	
	%saco el delta para la ultima capa (caso especial)

	delta{k} = (sTraining(i)-v{k+1})*(applyGprima(b, h{k+1}, LINEAL)+0.1);		
	%delta{k} = (sTraining(i)-v{k+1});
		
	%saco el delta para el resto de lsa capas usando la formula
	%w
	for p=k-1:-1:1
		waux = sacarUmbral(w{p+1});
		delta{p}=(applyGprima(b,h{p+1},FUNC)+0.1).*(delta{p+1}*waux);
	endfor
	%teniendo los delta calculo los dw para cada capa y actualizo todos los w
	for p=1:k
		dw=(e*delta{p}(:,:)'*v{p});
		w{p}=w{p}.+dw;
	endfor
	
	erroractual=(sTraining(i)-v{k+1})^2;
	if (isnan(erroractual))
		j
		ind
		i
		sTraining(i)
		k
		v
		w
		return
	endif
	erroracum+=erroractual;
  endfor
  erroracum=erroracum/2;
  if (mod(j,10)==0)
	j
	erroracum
	salida=aplicarRed(psiTraining,w,N,b);
	plot(psiTraining(:,1)',sTraining,"3;esperada;", psiTraining(:,1)',salida, "1;out;")
	drawnow
	fflush(1);
  endif
  if (erroracum<errorAnt)
	cantAciertos=cantAciertos+1;
	if (cantAciertos>=3)
		e=e+0.001;
		cantAciertos=0;
	endif
  endif
  if (erroracum>errorAnt)
	%w=wold;
	cantAciertos=0;
	e=e-(0.2*e);
  endif
  errorAnt=erroracum;  
  j++;
until(j==epocas || erroracum<=10e-3);
j
erroracum


%verifico si los resultados que obtengo con los pesos de la red se condicen con los que deberian dar segun la funcion
%estas dos funciones habria que unificarlas en un .m pasandole el psi que queremos evaluar, N y w
%printf("\n\nRESULTADOS ENTRENAMIENTO\n\n")
salida=aplicarRed(psiTraining,w,N,b);
plot(psiTraining(:,1)',sTraining,"3;esperada;", psiTraining(:,1)',salida, "1;out;"); %ploteo la funcion original y la salida para ver si aprendio la red

%printf("\n\nRESULTADOS TEST\n\n")
%for i=1:rows(psiTest)
%	patron = v{1}=psiTest(i,:); %seteo el inicial
%	for k=2:length(N)-1
%		v{k} = applyG(b,v{k-1}*w{k-1}', FUNC);
%		v{k} = agregarUmbral(v{k});
%	endfor
%	esperada = sTest(i)
%	salida = applyG(b,v{k}*w{k}', LINEAL) %'
%endfor

endfunction
